#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/un.h> 
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h> 
#include <arpa/inet.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <math.h>
#include <pthread.h>

#include "estructuras.h"
#include "constantes.h"
#include "estadisticas.h"
#include "listas.h"
#include "sockets.h"
#include "configuracion.h"
#include "CSCAN.h"
#include "NIPC.h"
#include "recibirComando.h"



void configToCHS();
chs getCHS(); 
void intacad ( int n , char* cadena); 
void * AtenderPedido (void * SocketConectado); 
void * AtenderPedido_CSCAN (void *args); 
void * RecibirPedido (void * SocketConectado); 
int removerConexionDeListaPFS(listaPFS ** pnodo, listaPFS ** ListaDePFS);  
   
chs glChs;
int sectores_totales_vda; 
char* algoritmo;
extern ListConfig * listaConfig;
extern int posicionCabezal;
extern pthread_mutex_t mutex;  
extern pthread_mutex_t mutex2; 
extern int activaEnAtencion;

int main()
{
	char *aux;
	int i,len,t,fdmax;
	char data_sector[512];
	int escucha_consola, Sock_consola;
	struct sockaddr_un local, remote;
	int iSocketConectado,iSocketDePedido;
	char *sAux;
	int puertoRAID;
	char* cadena;
	InfoIPC rInfoIPC;
	InfoIPC HS;
	InfoIPC respuestaHS;
	fd_set master,read_fds,set_socketFSS;
	char *payload;
	pid_t child;
	colas cola;
	colas2 cola2;
	int iMayorSocket=0;
	
	listaIDDescriptors* NodoDescriptores;
	pthread_t ID_HiloRecibirPedidos;
    pthread_t ID_HiloAtenderPedidos;
	char *sIDdescriptor_delPedido;
	char *sSocket;
	int sector;
	int ret;
	char* sNumeroSector;
	int numeroPedido;  /*Esto va si la conexion esta en modo LISTEN*/
	int iSocketEscuchaConexiones;
	int iSocketCliente_temporal;
	struct sockaddr_in cliente;
	int address_size=sizeof(struct sockaddr_in);
	int iSocketSINCRO;
	
	sAux=(char *)malloc(200);
	cola.cabeza=NULL; 
	cola.cola=NULL;
	cola2.activacabeza=NULL;
	cola2.activacola=NULL;
	cola2.pasivacabeza=NULL;
	cola2.pasivacola=NULL;
	sIDdescriptor_delPedido=(char *)malloc(20);
	sSocket=(char *)malloc(20);
	char *auxSock=(char *)malloc(20);
	
	
    listaConfig = NULL;
    cargarConfig(); 
    
	system("clear");	
		
	printf("\nCargando Archivo de Configuracion...\n\n");
    printf("Path_SocketConsola %s \n",obtenerValor("path"));
    printf("Modo %s \n",obtenerValor("modo"));
    printf("Algoritmo %s \n",obtenerValor("alg"));
    printf("Puerto %s \n",obtenerValor("puerto"));
    printf("Ip %s \n",obtenerValor("ip"));
    printf("Flag_Log %s \n",obtenerValor("flag_Log"));
    printf("Id_Disco %s \n",obtenerValor("id_Disco"));
    printf("CHS %s \n",obtenerValor("CHS"));
    printf("T_Lectura %s \n",obtenerValor("t_Lectura"));
    printf("T_Escritura %s \n",obtenerValor("t_Escritura"));
    printf("RPM %s \n",obtenerValor("RPM"));
    printf("T_Salto %s \n",obtenerValor("t_Salto"));
	printf("ipRAID %s \n",obtenerValor("ipRAID"));
	printf("Puerto CONEXION RAID %i \n",obtenerValor("puertoRAID"));
	printf("Puerto SINCRONIZACION %i \n",obtenerValor("puertoSinc"));
	
	configToCHS();	
	
	printf("glChs.cilindros %i \n",glChs.cilindros);
	printf("glChs.cabezal %i \n",glChs.cabezal);
	printf("glChs.sectores %i \n",glChs.sectores);
	sectores_totales_vda = sectoresTotales(getCHS());
	printf("Los sectores totales son %i \n",sectores_totales_vda);

	posicionCabezal=0;
	initialize_disk();
	
	if (strcmp(obtenerValor("modo"), "CONNECT") == 0){
		
		iSocketConectado = crearSocket(AF_INET);
		Conectar2(iSocketConectado,obtenerValor("ipRAID"),atoi(obtenerValor("puertoRAID")));	
		rInfoIPC.payLoadLength=strlen(obtenerValor("id_Disco"))+1;
		generarStructIPC(obtenerValor("id_Disco"),HANDSHAKE,&rInfoIPC);
		enviarIPC(iSocketConectado,&rInfoIPC);
		recibirIPC(iSocketConectado,&rInfoIPC);

		if(rInfoIPC.payLoadDescriptor== HSOK){
		
			intacad(glChs.cilindros,sAux);
			rInfoIPC.payLoadLength=200;
			generarStructIPC(sAux,NUEVO_DA,&rInfoIPC);
			enviarIPC(iSocketConectado,&rInfoIPC);
			
			intacad(glChs.cabezal,sAux);
			rInfoIPC.payLoadLength=200;
			generarStructIPC(sAux,NUEVO_DA,&rInfoIPC);
			enviarIPC(iSocketConectado,&rInfoIPC);
			
			intacad(glChs.sectores, sAux);
			rInfoIPC.payLoadLength=200;
			generarStructIPC(sAux,NUEVO_DA,&rInfoIPC);
			enviarIPC(iSocketConectado,&rInfoIPC);
			
			free(sAux);
			
			recibirIPC(iSocketConectado,&rInfoIPC);

			switch(rInfoIPC.payLoadDescriptor)
			{
				case DA_INCOMPATIBLE:
						printf("PPD incompatible con el que se quiere conectar\n");
						return 0;
						break;
		
				case DA_CONECTADO_CORRECTAMENTE:
					printf("El PPD se conecto correctamente \n");
					break;

				default:
					printf("No se recibio msj valido \n");
					return 0;
					break;
					
			}
		}

		
		ret=pthread_create(&ID_HiloAtenderPedidos,NULL,(void *)AtenderPedido,(void *)&cola2); 
		if(ret!=0){
			perror("pthread_create");
			exit(EXIT_FAILURE);
		}
		  	
		while(1)
		{
			FD_ZERO(&master);
			FD_SET(iSocketConectado, &master);
			if (iSocketConectado>iMayorSocket)
				iMayorSocket = iSocketConectado;
			
			select(iMayorSocket + 1 , &master, NULL, NULL, NULL);	
	
			if( FD_ISSET(iSocketConectado,&master) > 0 )
			{
				
				NodoDescriptores = (struct ListaIDdescriptors *)malloc(sizeof(struct ListaIDdescriptors));	
				
				/*Se recibe numero de pedido*/
				InfoIPC rInfoIPC2;
				recibirIPC(iSocketConectado,&rInfoIPC2);	
				strcpy(NodoDescriptores->sDescriptorID_delPedido,rInfoIPC2.payload);
				
				/*Se recibe numero del sector*/
				InfoIPC rInfoIPC3;
				recibirIPC(iSocketConectado,&rInfoIPC3);			
				NodoDescriptores->sector=atoi(rInfoIPC3.payload);
				
				/*Se completa el nodo*/
				intacad(iSocketConectado,sSocket);
				strcpy(NodoDescriptores->sSocketPorElCualResponder,sSocket);
				NodoDescriptores->Sgt = NULL;
				NodoDescriptores->Ant = NULL;        
				
				
				if((rInfoIPC3.payLoadDescriptor == READ_SECTOR_REQUEST)||(rInfoIPC3.payLoadDescriptor == READ_SECTORSYNC_REQUEST))
				{
					strcpy(NodoDescriptores->data,"");
					NodoDescriptores->operacion=0;
				}
				
				if((rInfoIPC3.payLoadDescriptor == WRITE_SECTOR_REQUEST)||(rInfoIPC3.payLoadDescriptor == WRITE_SECTORSYNC_REQUEST))
				{
					InfoIPC rInfoIPC4;
					recibirIPC(iSocketConectado,&rInfoIPC4);						
					memcpy(NodoDescriptores->data,rInfoIPC4.payload,512);
					NodoDescriptores->operacion=1;
				}
				
				if((rInfoIPC3.payLoadDescriptor == READ_SECTOR_REQUEST))
					printf("REQUEST DE LECTURA\n");
				if((rInfoIPC3.payLoadDescriptor == READ_SECTORSYNC_REQUEST))
					printf("REQUEST DE LECTURASYNC\n");
				if((rInfoIPC3.payLoadDescriptor == WRITE_SECTOR_REQUEST))
					printf("REQUEST DE ESCRITURA\n");
				if((rInfoIPC3.payLoadDescriptor == WRITE_SECTORSYNC_REQUEST))
					printf("REQUEST DE ESCRITURASYNC\n");
				
				
				if(!strcmp(obtenerValor("alg"),"FSCAN"))
				{
					if(activaEnAtencion)
					{	
			
						if(pthread_mutex_lock(&mutex2)!=0){
							perror("pthread_mutex_lock");
							exit(EXIT_FAILURE);
						}

						AgregarAColaDePedidosADisco(&NodoDescriptores, &cola2.pasivacabeza, &cola2.pasivacola);
						printf("COLA PASIVA \n");
						printf("----------- \n");
						imprimirCola(cola2.pasivacabeza);
						printf("\n");
				
						if(pthread_mutex_unlock(&mutex2)!=0){
							perror("pthread_mutex_unlock");
							exit(EXIT_FAILURE);
						}	
					}
					else
					{	
				
						if(pthread_mutex_lock(&mutex)!=0){
							perror("pthread_mutex_lock");
							exit(EXIT_FAILURE);
						}
				
						AgregarAColaDePedidosADisco(&NodoDescriptores, &cola2.activacabeza, &cola2.activacola);
						printf("COLA ACTIVA \n");
						printf("----------- \n");
						imprimirCola(cola2.activacabeza);
						printf("\n");
				
						if(pthread_mutex_unlock(&mutex)!=0){
							perror("pthread_mutex_unlock");
							exit(EXIT_FAILURE);
						}	
					}
					NodoDescriptores=NULL;
				
				}
				
				/*
				if(!strcmp(obtenerValor("alg"),"CSCAN"))
				{
				
					if(pthread_mutex_lock(&mutex)!=0){
						perror("pthread_mutex_lock");
						exit(EXIT_FAILURE);
					}
				
					AgregarAColaDePedidosADisco(&NodoDescriptores, &cola.cabeza, &cola.cola);
					printf("Tras agregar a colaActiva de pedidos a disco \n");
					/*ordenarListaCSCAN(&cola.cabeza,&cola.cola,posicionCabezal);*/					
					/*imprimirCola(cola.cabeza);
				
				
					if(pthread_mutex_unlock(&mutex)!=0){
						perror("pthread_mutex_unlock");
						exit(EXIT_FAILURE);
					}	
				
				}*/
				
			}
			
		}
			
		cerrarSocket(iSocketConectado);
	}	

	
		ret=pthread_create(&ID_HiloAtenderPedidos,NULL,(void *)AtenderPedido_CSCAN,(void *)&cola); 
		if(ret!=0){
			perror("pthread_create");
			exit(EXIT_FAILURE);
		}
		printf("Activa en atencion %i \n",activaEnAtencion);
		
		
	if(!strcmp(obtenerValor("modo"), "LISTEN")){	
	
		
		int rc=0;
		numeroPedido=1;
		NodoDescriptores = (struct ListaIDdescriptors *)malloc(sizeof(struct ListaIDdescriptors));	
		int iSocketPFS_temporal;
		iSocketEscuchaConexiones  = PrepararSocket (atoi(obtenerValor("puerto")),obtenerValor("ip"));   
		escuchar(iSocketEscuchaConexiones,10000); 
		printf("iSocketEscuchaConexiones  %i \n",iSocketEscuchaConexiones);
		
		int maxSocket;
			
		listaPFS *ListaPFSAbiertos = NULL;
		listaPFS *pfs = NULL;
		
		while(1){
		
			FD_ZERO(&set_socketFSS);
			FD_SET(iSocketEscuchaConexiones ,&set_socketFSS);
			maxSocket = iSocketEscuchaConexiones;
			
			pfs = ListaPFSAbiertos;
			printf("\nClientes en Select: \n");
			
			while(pfs != NULL)
			{
				FD_SET(pfs->iSocket ,&set_socketFSS);
				printf("%i->", pfs->iSocket);
				if(maxSocket < pfs->iSocket)
					maxSocket = pfs->iSocket;
				pfs = pfs->Sgt;
			}
			printf("NULL\n");
			/*if(ListaPFSAbiertos != NULL)
				sleep(1);*/
			
			select(maxSocket+1, &set_socketFSS, NULL, NULL, NULL);
			
			pfs = ListaPFSAbiertos;
			while(pfs != NULL)
			{
				if( FD_ISSET(pfs->iSocket , &set_socketFSS ) > 0 ){
					break;
				}
				pfs = pfs->Sgt;
			}
			if(pfs != NULL)
			{
				while(1)
				{
				printf("Cambio Socket Cliente PFS %i \n", pfs->iSocket);
				rc=0;
				rc = recibirIPC(pfs->iSocket,&rInfoIPC);
				if (rc < 0)
				{
					printf("  recibirIPC() failed\n");
					pfs->flag_cerrar = 1;
					break;
				}
				else
				{
					intacad(numeroPedido,sIDdescriptor_delPedido); 
					intacad(pfs->iSocket,sSocket);    
					strcpy(NodoDescriptores->sDescriptorID_delPedido , sIDdescriptor_delPedido);
					printf("Cliente PFS %s \n", sSocket);   
					strcpy(NodoDescriptores->sSocketPorElCualResponder , sSocket);
					NodoDescriptores->Sgt = NULL;
					NodoDescriptores->Ant = NULL;      
						
						
					if((rInfoIPC.payLoadDescriptor == READ_SECTOR_REQUEST) || (rInfoIPC.payLoadDescriptor == READ_SECTOR_REQUEST_LAST))
					{
						printf("\nREAD_SECTOR_REQUEST\n");
						NodoDescriptores->operacion = 0;
						NodoDescriptores->sector=atoi(rInfoIPC.payload);  
				
					}
					else if(rInfoIPC.payLoadDescriptor == WRITE_SECTOR_REQUEST)
					{
					
						printf("\nWRITE_SECTOR_REQUEST\n");
						NodoDescriptores->sector=atoi(rInfoIPC.payload);
						
						rInfoIPC.payLoadLength = 5;
						generarStructIPC("nada",RTA_WRITE_SECTOR_REQUEST,&rInfoIPC);
						enviarIPC(pfs->iSocket,&rInfoIPC);
						
						rc = recibirIPC(pfs->iSocket,&rInfoIPC);
						if(!(rInfoIPC.payLoadDescriptor == SET_SECTORES  || rInfoIPC.payLoadDescriptor == SET_SECTORES_LAST))
						{
							printf("  recibirIPC2() failed\n");
							pfs->flag_cerrar = 1;
							break;
						}
						
						
						intacad(numeroPedido,sIDdescriptor_delPedido); 
						intacad(pfs->iSocket,sSocket);          
						strcpy(NodoDescriptores->sDescriptorID_delPedido , sIDdescriptor_delPedido);
						strcpy(NodoDescriptores->sSocketPorElCualResponder , sSocket);
						NodoDescriptores->operacion = 1;
						memset(NodoDescriptores->data,'\0',512);
						memcpy(NodoDescriptores->data, rInfoIPC.payload, 512);
						
						printf("\nWRITE_SECTOR_REQUEST2\n");
				
					}
					
					if(pthread_mutex_lock(&mutex)!=0){
						printf("pthread_mutex_lock\n");
						exit(EXIT_FAILURE);
					}
				
					AgregarAColaDePedidosADisco(&NodoDescriptores, &cola.cabeza, &cola.cola);	
				
					ordenarListaCSCAN(&cola.cabeza,&cola.cola,posicionCabezal);
				
					imprimirCola(cola.cabeza);
				
					if(pthread_mutex_unlock(&mutex)!=0){
						printf("pthread_mutex_unlock\n");
						exit(EXIT_FAILURE);
					}	
					
					if((rInfoIPC.payLoadDescriptor == READ_SECTOR_REQUEST_LAST) || (rInfoIPC.payLoadDescriptor == SET_SECTORES_LAST))
					{
						printf("  SALGO\n");
						break;
					}
				}
				}
				if(pfs->flag_cerrar == 1)
				{
					FD_CLR(pfs->iSocket, &set_socketFSS);
					close(pfs->iSocket);
					removerConexionDeListaPFS(&pfs, &ListaPFSAbiertos);
				}
				numeroPedido++;
			}
			
			
			if(FD_ISSET(iSocketEscuchaConexiones , &set_socketFSS ) >0)
			{
				if((iSocketPFS_temporal = accept(iSocketEscuchaConexiones,(struct sockaddr*)&cliente,&address_size))== -1) 
					return ERRORACCEPT;	
            		
				printf("\nSe conecto cliente.El descriptor temporal devuelto es: %i \n", iSocketPFS_temporal);   
				recibirIPC(iSocketPFS_temporal,&rInfoIPC);
				/*if (rc < 0)
				{
					close(iSocketPFS_temporal);
					printf("  recibirIPC() failed\n");
				}
				else*/
				/*if(rInfoIPC.payLoadDescriptor == HANDSHAKE)
				{*/
					InfoIPC hsRInfoIPC;
					hsRInfoIPC.payLoadLength = 5;
					generarStructIPC("nada",HSOK,&hsRInfoIPC);
					enviarIPC(iSocketPFS_temporal,&hsRInfoIPC);

					listaPFS *nuevoPFS = (listaPFS *) malloc (sizeof(listaPFS));
					nuevoPFS->iSocket = iSocketPFS_temporal;
					nuevoPFS->flag_cerrar = 0;
					nuevoPFS->Sgt =NULL;
					AgregarAColaDeConexionesPFS(&nuevoPFS, &ListaPFSAbiertos);
					FD_SET(nuevoPFS->iSocket ,&set_socketFSS);
					if(maxSocket < nuevoPFS->iSocket)
						maxSocket = nuevoPFS->iSocket;
				/*}*/
			}
		}
	}
	
	return 0;
}


void configToCHS()
{
	/* No recibe parámetros y devuelve tres enteros correspondientes a la información física obtenida de archivo de configuración. */
	char* ntok;
	char* chsAux;
	chs resultadoChs;
	
	chsAux = (char*)malloc(strlen(obtenerValor("CHS"))+1);
	strcpy(chsAux,obtenerValor("CHS"));

	resultadoChs.cilindros = atoi(strtok(chsAux,","));
	resultadoChs.cabezal = atoi(strtok(NULL,","));
	resultadoChs.sectores = atoi(strtok(NULL,","));
	
	glChs = resultadoChs;
}

chs getCHS()
{
	return glChs;
}


void intacad ( int n , char* cadena)
{	
    int j=0;
    int i=0;
	char aux;

	do
    {
		cadena[i] = (n % 10) + '0';    
		n=n/10;
		i++;
	}while (n > 0);

    cadena[i] = '\0';
    i--;

	while(i>j)
	{
		aux=cadena[i];
		cadena[i]=cadena[j];
		cadena[j]=aux;
		i--;
		j++;
	}

}


int	AgregarAColaDeConexionesPFS(listaPFS ** pnodo, listaPFS ** ListaDePFS)
{	
	if((*ListaDePFS) == NULL)
	{
		(*pnodo)->Sgt = NULL;
		(*ListaDePFS) = (*pnodo);
	}	
	else
	{
		(*pnodo)->Sgt = *ListaDePFS;
		*ListaDePFS = *pnodo;
	}
	
}

int removerConexionDeListaPFS(listaPFS ** pnodo, listaPFS ** ListaDePFS)
{
	listaPFS *actual;
	actual= *ListaDePFS;
	if(actual != NULL)
	{
		if(actual->iSocket == (*pnodo)->iSocket)
		{
			*ListaDePFS=(*ListaDePFS)->Sgt;
			free(actual);
			return 1;
		}
		
		listaPFS *anterior = *ListaDePFS;
		actual = actual->Sgt;
		
		while(actual != NULL)
		{
			if(actual->iSocket == (*pnodo)->iSocket)
				break;
			anterior = actual;
			actual = actual->Sgt;
		}
		if(actual != NULL)
		{
			anterior->Sgt = actual->Sgt;
			free(actual);
		}
	}
	return 1;
}








